#include <kuai/misc/error.hpp>
#include <kuai/misc/job.hpp>

namespace kuai {

	void SequenceJob::parse(std::istream& isInput, std::ostream& osOutput, std::ostream& osError) {
		StringMap par;
		String line;
		while (getline(isInput, line)) {
			if (isRemark(line)) {
				continue;
			}
			else if (isEndJob(line)) {
				break;
			}
			else if (isAbort(line)) {
				osOutput << "Job abort" << std::endl;
				return;
			}
			else {
				StringPair data = splitPair(line);
				boost::algorithm::to_lower(data.first);
				if (!data.first.empty()) {
					if (isEmbed(data.second)) {
						data.second = "";
						while (getline(isInput, line)) {
							if (isEnd(line)) {
								break;
							}
							else if (isEndJob(line)) {
								throw error("Block %1% is not closed", data.first);
							}
							else {
								data.second += line + '\n';
							}
						}
					}
					pushParameter(data.first, data.second, osOutput, osError);
				}
			}
		}
		doJob(osOutput, osError);
	}

	void ParameterJob::pushParameter(const String& key, const String& data, std::ostream& outlog, std::ostream& errlog) {
		if (parameters.find(key) == parameters.end()) {
			parameters.insert(std::make_pair(key, data));
		}
		else {
			throw error("Duplicate parameter %1%", key);
		}
	}
	
	void ParameterJob::doJob(std::ostream& stdlog, std::ostream& errlog) {
		doJob(stdlog, errlog, parameters);
		for (StringMap::const_iterator
			it = parameters.begin(); it != parameters.end(); ++it)
		{
			dumpWarning(error("Useless Parameter %1%", it->first), errlog);
		}
	}
	
	size_t ParameterJob::getParameter(StringMap& par, const String& key, size_t def) {
		StringMap::iterator it = par.find(key);
		size_t result = def;
		if (it != par.end()) {
			result = lexical_cast<size_t>(it->second);
			par.erase(it);
		}
		return result;
	}
	RealNumber ParameterJob::getParameter(StringMap& par, const String& key, RealNumber def) {
		StringMap::iterator it = par.find(key);
		RealNumber result = def;
		if (it != par.end()) {
			result = lexical_cast<RealNumber>(it->second);
			par.erase(it);
		}
		return result;
	}

	String ParameterJob::getParameter(StringMap& par, const String& key, String def) {
		StringMap::iterator it = par.find(key);
		String result = def;
		if (it != par.end()) {
			result = it->second;
			par.erase(it);
		}
		return result;
	}
	String ParameterJob::getParameter(StringMap& par, const String& key, const Char def[]) {
		StringMap::iterator it = par.find(key);
		String result = def;
		if (it != par.end()) {
			result = it->second;
			par.erase(it);
		}
		return result;
	}

	bool ParameterJob::getParameter(StringMap& par, const String& key, bool def) {
		StringMap::iterator it = par.find(key);
		bool result = def;
		if (it != par.end()) {
			result = lexical_cast<bool>(it->second);
			par.erase(it);
		}
		return result;
	}

	String ParameterJob::getParameter(StringMap& par, const String& key) {
		StringMap::iterator it = par.find(key);
		if (it == par.end()) {
			throw error("You should assign parameter %1%", key);
		}
		else {
			String result = it->second;
			par.erase(it);
			return result;
		}
	}
	
	JobManager::JobManager()
	{ }

	void JobManager::parse(std::istream& input, std::ostream& output, std::ostream& stream_error) const {
		String line;
		while (getline(input, line)) {
			boost::trim_left(line);
			if (boost::istarts_with(line, "job")) {
				StringPair tokens = splitPair(line);
				if (tokens.first.size() == 3) {
					String command = uniformCommand(tokens.second);
					const BasicJob::BasicJobCreator* creator = get(command);
					if (creator == NULL) {
						throw error("Unknown Job Type %1%", tokens.second);
					}
					else {
						Time t1 = now();
						output << "[Job " << command << "] started at: " << t1 << std::endl;
						try {
							boost::shared_ptr<BasicJob> job = creator->create();
							job->parse(input, output, stream_error);
						}
						catch (std::exception& err) {
							dumpError(err, stream_error);
						}
						Time t2 = now();
						output  << "[Job " << command << "] finished at: " << t2 
								<< "\n[Job " << command << "] cost " << t2-t1 << "\n\n\n\n" << std::endl;
					}
				}
			}
			else if (boost::algorithm::istarts_with(line, "help")) {
				StringPair tokens = splitPair(line);
				if (tokens.first.size() == 4) {
					String command = uniformCommand(tokens.second);
					if (tokens.second.empty()) {
						output << "Typing help=command to get the detail message\n"
							<< "Available command including \n";
						for (JobManager::const_iterator 
							it = begin(); it != end(); ++it)
						{
							output << "    " << it->first << std::endl;
						}
					}
					else {
						const BasicJob::BasicJobCreator* creator = get(command);
						if (creator == NULL) {
							throw error("Unknown Job Type %1%", tokens.second);
						}
						else {
							output << "JOB=" << command << std::endl;
							creator->help(output);
							output  << "\n\n\n" << std::endl;
						}
					}
				}
			}
			else if (boost::algorithm::iequals(boost::algorithm::trim_copy(line), "quit")) {
				break;
			}
		}
	}

	JobManager& JobManager::getInstance() {
		static JobManager singleton;
		return singleton;
	}

	String JobManager::uniformCommand(const String& command) {
		String result;
		StringArray tokens = split(command);
		for (StringArray::iterator
			it = tokens.begin(); it != tokens.end(); ++it)
		{
			boost::algorithm::to_lower(*it);
			result += *it + " ";
		}
		boost::algorithm::trim_right(result);
		return result;
	}

}
